package com.sfy.core.example.proxy;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.sfy.core.example.RpcApplication;
import com.sfy.core.example.config.RpcConfig;
import com.sfy.core.example.constant.RpcConstant;
import com.sfy.core.example.fault.retry.RetryStrategy;
import com.sfy.core.example.fault.retry.RetryStrategyFactory;
import com.sfy.core.example.fault.tolerant.TolerantStrategy;
import com.sfy.core.example.fault.tolerant.TolerantStrategyFactory;
import com.sfy.core.example.fault.tolerant.TolerantStrategyKeys;
import com.sfy.core.example.loadbalancer.LoadBalancer;
import com.sfy.core.example.loadbalancer.LoadBalancerFactory;
import com.sfy.core.example.model.RpcRequest;
import com.sfy.core.example.model.RpcResponse;
import com.sfy.core.example.model.ServiceMetaInfo;
import com.sfy.core.example.protocol.*;
import com.sfy.core.example.registry.Registry;
import com.sfy.core.example.registry.RegistryFactory;
import com.sfy.core.example.serializer.Serializer;
import com.sfy.core.example.serializer.SerializerFactory;
import com.sfy.core.example.service.tcp.VertxTcpClient;
import lombok.extern.slf4j.Slf4j;


import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;


/**
 * 实现动态代理
 */
@Slf4j
public class ServiceProxy implements InvocationHandler {

    /**
     * 调用代理
     *
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 指定序列化器
        final Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer());

        // 构造请求
        String serviceName = method.getDeclaringClass().getName();
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(serviceName)
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        try {
            // 序列化
            byte[] bodyBytes = serializer.serialize(rpcRequest);
            // 从注册中心获取服务提供者请求地址
            RpcConfig rpcConfig = RpcApplication.getRpcConfig();
            Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());
            ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
            serviceMetaInfo.setServiceName(serviceName);
            serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
            // 此处服务发现代码逻辑有点问题，后序进行排查 TODO
            List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
            log.info("serviceMetaInfoList:{}",serviceMetaInfoList);
            if (CollUtil.isEmpty(serviceMetaInfoList)) {
                log.info("获取到的ServiceKey的值为:{}",serviceMetaInfo.getServiceKey());
                throw new RuntimeException("获取不到服务地址的值");
            }
            // 负载均衡、
            LoadBalancer loadBalancer = LoadBalancerFactory.getInstance(rpcConfig.getLoadBalancer());
            Map<String,Object> requestParams = new HashMap<>();
            requestParams.put("methodName",rpcRequest.getMethodName());
            /*
               此处应当获取的是serviceMetaInfoList的值去实现多服务的负载均衡
               但是在服务发现时由于逻辑问题获取不到serviceMetaInfoList的列表，所以此处
               简化开发后：只使用serviceMetaInfo单个服务节点信息，负载均衡只处理一台服务器 后序进行优化
               TODO
             */
            //ServiceMetaInfo selectServiceMetaInfo = loadBalancer.select(requestParams,serviceMetaInfoList)
            ServiceMetaInfo selectServiceMetaInfo = loadBalancer.select(requestParams,serviceMetaInfo);
            RpcResponse rpcResponse = null;
            try{
                // 使用重试机制
                RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetry());
                rpcResponse = retryStrategy.doRetry(()->
                        VertxTcpClient.doRequest(rpcRequest, selectServiceMetaInfo)
                );
            }catch (Exception e){
                TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(rpcConfig.getTolerantStrategy());
                tolerantStrategy.doTolerant(null,e);
            }
            return rpcResponse.getData();
        } catch (Exception e) {
            log.info("调用失败，失败信息为{}",e.getMessage());
            throw new RuntimeException("调用失败");
        }
    }


    private static RpcResponse doHttpRequest(ServiceMetaInfo selectedServiceMetaInfo, byte[] bodyBytes) throws IOException {
        final Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer());
        // 发送 HTTP 请求
        try (HttpResponse httpResponse = HttpRequest.post(selectedServiceMetaInfo.getServiceAddress())
                .body(bodyBytes)
                .execute()) {
            byte[] result = httpResponse.bodyBytes();
            // 反序列化
            RpcResponse rpcResponse = serializer.deserialize(result, RpcResponse.class);
            return rpcResponse;
        }
    }
}